package com.qyer.search.model;

import static com.qyer.commons.utils.CommonUtils.cast2Int;
import static com.qyer.commons.utils.CommonUtils.cast2String;
import static com.qyer.search.enums.SearchError.INVALID_PARAMETER;
import static com.qyer.search.model.PageInfo.buildPageInfo;
import static com.qyer.search.model.User.buildUser;
import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Sets;
import com.qyer.commons.utils.CommonUtils;
import com.qyer.search.Constants;
import com.qyer.search.cache.mutable.MutableCaches;
import com.qyer.search.exception.InvalidParameterException;
import com.qyer.search.servlet.AbstractServlet;
import com.qyer.search.utils.SplitStringUtils;
import com.qyer.words.model.Word;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.omg.CORBA.PUBLIC_MEMBER;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by tangzheng on 16/3/7.
 */
public class SearchParameterOrigin {

  private static final Set<String> LOG_ESCAPE_PARAM = Sets
    .newHashSet("kw", "type1", "type2", "f_country_id", "f_city_id", "uid", "guid", "context",
                "source_place", "supplier_id");

  // 单个一级类目
  @JsonProperty
  protected String type1;

  // 单个二级类目
  @JsonProperty
  protected String type2;

  // 当个目的地国家id
  @JsonProperty
  protected int destinationCountryId;

  // 单个目的地城市id
  @JsonProperty
  protected int destinationCityId;

  @JsonProperty
  protected int continentId;

  //出发地id
  @JsonProperty
  protected int setOutPlaceId;

  @JsonProperty
  protected String destination;
  @JsonProperty
  protected String destCountry;

  @JsonIgnore
  protected boolean desEx;
  @JsonIgnore
  protected boolean voucherEx;

  //供应商id
  @JsonProperty
  protected int supplierId;

  // 用户输入的关键词
  @JsonProperty
  protected String userInputKW;

  // 额外附加参数
  @JsonProperty("additional_params")
  protected Map<String, Object> conditions;
  @JsonIgnore
  protected String context;
  @JsonIgnore
  protected String requestId;
  @JsonIgnore
  protected User user;
  @JsonIgnore
  protected PageInfo pageInfo;
  @JsonProperty
  protected String setOutString;

  protected Set<Integer> type1s;
  protected Set<Integer> type2s;

  public SearchParameterOrigin() {
    this.context = "inner-test";
    this.requestId = CommonUtils.getRequestId();
  }

  public SearchParameterOrigin(HttpServletRequest req) throws Exception{

    this.context = cast2String(req.getParameter("context"));

    this.type1 = StringUtils.trimToNull(req.getParameter("type1"));
    this.type2 = StringUtils.trimToNull(req.getParameter("type2"));

    type1s = SplitStringUtils.splitToSet(type1, Constants.SEPARATOR_PARAMETER);
    type2s = SplitStringUtils.splitToSet(type2, Constants.SEPARATOR_PARAMETER);

    this.destinationCountryId = cast2Int(req.getParameter("f_country_id"));
    this.destinationCityId = cast2Int(req.getParameter("f_city_id"));
    this.userInputKW = cast2String(req.getParameter("kw"));

    setOutString = cast2String(req.getParameter("source_place"));
    this.setOutPlaceId = -1;
    if(StringUtils.isNotBlank(setOutString)){
      setOutString = setOutString.trim();
      this.setOutPlaceId = Integer.MAX_VALUE;
      if(Constants.START_POS_MAP.containsKey(setOutString))
        setOutPlaceId = Constants.START_POS_MAP.get(setOutString);
    }

    this.supplierId = cast2Int(req.getParameter("supplier_id"));

    if (StringUtils.isNotBlank(this.userInputKW)) {
      this.userInputKW = this.userInputKW.toLowerCase();
    }
    this.requestId = CommonUtils.getRequestId();
    this.user = buildUser(req);
    this.pageInfo = buildPageInfo(req);

    Map parameterMap = req.getParameterMap();
    if (MapUtils.isEmpty(parameterMap)) {
      this.conditions = Collections.emptyMap();
    } else {
      this.conditions = new HashMap<>(5);
      Set<Map.Entry> s = parameterMap.entrySet();
      String k;
      Object valueObject;
      for (Map.Entry entry : s) {
        k = entry.getKey().toString().toLowerCase();
        /*
        if (LOG_ESCAPE_PARAM.contains(k)) {
          continue;
        }*/
        valueObject = entry.getValue();
        if (valueObject == null) {
          continue;
        }
        if (valueObject.getClass().isArray()) {
          Object[] objArr = (Object[]) valueObject;
          if (objArr != null && objArr.length == 1) {
            conditions.put(k, String.valueOf(objArr[0]));
          } else {
            conditions.put(k, objArr);
          }
        } else {
          conditions.put(k, String.valueOf(valueObject));
        }
      }
    }
  }
  public SearchParameterOrigin(HttpServletRequest req, HttpServletResponse resp, AbstractServlet abstractServlet) throws Exception{

    this.requestId = CommonUtils.getRequestId();
    this.conditions = null;
    this.context = cast2String(req.getParameter("context"));

    this.type1 = StringUtils.trimToNull(req.getParameter("type1"));
    this.type2 = StringUtils.trimToNull(req.getParameter("type2"));
    try {
      type1s = SplitStringUtils.splitToSet(type1, Constants.SEPARATOR_PARAMETER);
      type2s = SplitStringUtils.splitToSet(type2, Constants.SEPARATOR_PARAMETER);

      this.destinationCountryId = cast2Int(req.getParameter("f_country_id"));
      this.destinationCityId = cast2Int(req.getParameter("f_city_id"));
      this.userInputKW = cast2String(req.getParameter("kw"));
      this.destination = cast2String(req.getParameter("dest"));
      this.destCountry = cast2String(req.getParameter("destCountry"));

      String setOutString = cast2String(req.getParameter("source_place"));
      this.setOutPlaceId = -1;
      if (StringUtils.isNotBlank(setOutString)) {
        setOutString = setOutString.trim();
        this.setOutPlaceId = Integer.MAX_VALUE;
        if (Constants.START_POS_MAP.containsKey(setOutString))
          setOutPlaceId = Constants.START_POS_MAP.get(setOutString);
      }

      this.supplierId = cast2Int(req.getParameter("supplier_id"));

      if (StringUtils.isNotBlank(this.userInputKW)) {
        this.userInputKW = this.userInputKW.toLowerCase();
      }

      this.user = buildUser(req);
      this.pageInfo = buildPageInfo(req);

      Map parameterMap = req.getParameterMap();
      if (MapUtils.isEmpty(parameterMap)) {
        this.conditions = Collections.emptyMap();
      } else {
        this.conditions = new HashMap<>(5);
        Set<Map.Entry> s = parameterMap.entrySet();
        String k;
        Object valueObject;
        for (Map.Entry entry : s) {
          k = entry.getKey().toString().toLowerCase();

          if (LOG_ESCAPE_PARAM.contains(k)) {
            continue;
          }
          valueObject = entry.getValue();
          if (valueObject == null) {
            continue;
          }
          if (valueObject.getClass().isArray()) {
            Object[] objArr = (Object[]) valueObject;
            if (objArr != null && objArr.length == 1) {
              conditions.put(k, String.valueOf(objArr[0]));
            } else {
              conditions.put(k, objArr);
            }
          } else {
            conditions.put(k, String.valueOf(valueObject));
          }
        }
      }
    }catch (Exception e){
      abstractServlet.writeError(requestId, context, conditions, resp, System.currentTimeMillis(), INVALID_PARAMETER, e);
      throw e;
    }
    if(!isNullParameter()){
      this.type1 = "1016,1018,2410,1785";
      this.type1s.add(1016);
      this.type1s.add(1018);
      this.type1s.add(2410);
      this.type1s.add(1785);
    }
  }
  /*public int getConditionCacheKey() {
    int result = type1;
    result = 31 * result + type2;
    result = 31 * result + destinationCountryId;
    result = 31 * result + destinationCityId;
    result = 31 * result + setOutPlaceId;
    result = 31 * result + supplierId;
    return result;
  }*/



  public String getType1() {
    return type1;
  }

  public void setType1(String type1) {
    this.type1 = type1;
  }

  public String getType2() {
    return type2;
  }

  public void setType2(String type2) {
    this.type2 = type2;
  }

  public int getDestinationCountryId() {
    return destinationCountryId;
  }

  public void setDestinationCountryId(int destinationCountryId) {
    this.destinationCountryId = destinationCountryId;
  }

  public int getDestinationCityId() {
    return destinationCityId;
  }

  public void setDestinationCityId(int destinationCityId) {
    this.destinationCityId = destinationCityId;
  }

  public String getDestination() {
    return destination;
  }

  public boolean isDesEx() {
    return desEx;
  }

  public void setDesEx(boolean desEx) {
    this.desEx = desEx;
  }

  public boolean isVoucherEx() {
    return voucherEx;
  }

  public void setVoucherEx(boolean voucherEx) {
    this.voucherEx = voucherEx;
  }

  public void setDestination(String destination) {
    this.destination = destination;
  }

  public String getUserInputKW() {
    return userInputKW;
  }

  public void setUserInputKW(String userInputKW) {
    this.userInputKW = userInputKW;
  }

  public String getContext() {
    return context;
  }

  public void setContext(String context) {
    this.context = context;
  }

  public String getRequestId() {
    return requestId;
  }

  public void setRequestId(String requestId) {
    this.requestId = requestId;
  }

  public User getUser() {
    return user;
  }

  public void setUser(User user) {
    this.user = user;
  }

  public PageInfo getPageInfo() {
    return pageInfo;
  }

  public void setPageInfo(PageInfo pageInfo) {
    this.pageInfo = pageInfo;
  }

  public Map<String, Object> getConditions() {
    return conditions;
  }

  public void setConditions(Map<String, Object> conditions) {
    this.conditions = conditions;
  }

  public int getSetOutPlaceId() {
    return setOutPlaceId;
  }

  public void setSetOutPlaceId(int setOutPlaceId) {
    this.setOutPlaceId = setOutPlaceId;
  }

  public int getSupplierId() {
    return supplierId;
  }

  public void setSupplierId(int supplierId) {
    this.supplierId = supplierId;
  }

  public String getDestCountry() {
    return destCountry;
  }

  public void setDestCountry(String destCountry) {
    this.destCountry = destCountry;
  }

  public void setDestCountryIf(String destCountry){
    if(this.destCountry == null){
      this.destCountry = destCountry;
    }
  }

  public void setDestIf(String dest){
    if(this.destination == null){
      this.destination = dest;
    }
  }

  public Set<Integer> getType1s() {
    return type1s;
  }

  public void setType1s(Set<Integer> type1s) {
    this.type1s = type1s;
  }

  public Set<Integer> getType2s() {
    return type2s;
  }

  public void setType2s(Set<Integer> type2s) {
    this.type2s = type2s;
  }

  public int getContinentId() {
    return continentId;
  }

  public void setContinentId(int continentId) {
    this.continentId = continentId;
  }

  public String getSetOutString() {
    return setOutString;
  }

  public void setSetOutString(String setOutString) {
    this.setOutString = setOutString;
  }

  @Override public String toString() {
    return new ToStringBuilder(this, SHORT_PREFIX_STYLE).append("type1", type1)
                                                        .append("type2", type2)
                                                        .append("destinationCountryId",
                                                                destinationCountryId)
                                                        .append("destinationCityId",
                                                                destinationCityId)
                                                        .append("setOutPlace",setOutPlaceId)
                                                        .append("supplierId",supplierId)
                                                        .append("userInputKW", userInputKW)
                                                        .append("context", context)
                                                        .append("requestId", requestId)
                                                        .append("user", user)
                                                        .append("pageInfo", pageInfo)
                                                        .append("conditions", conditions)
                                                        .toString();
  }

  public void reconstruct(String name){
    this.destination = name;
    this.userInputKW = null;
  }
  public void reconstructWithCity(String name){
    if(this.destination == null) {
      this.destination = name;
      //this.userInputKW = null;
    }
  }

  public void reconstructWithCity2(String name){
    if(this.destination == null) {
      this.destination = name;
      this.userInputKW = null;
    }
  }

  public void reconstructWithCountry(String name){
    if(this.destCountry == null) {
      this.destCountry = name;
      //this.userInputKW = null;
    }
  }

  public void reconstructWithCountry2(String name){
    if(this.destCountry == null) {
      this.destCountry = name;
      this.userInputKW = null;
    }
  }
  public List<SearchParameter> splitByType() throws Exception{
    Map<Integer, Integer> map = MutableCaches.getInstance().PRODUCT_TYPE_MAP.getCacheContent();
    List<SearchParameter> result = new ArrayList<>();
    Set<Integer> type1copy = new HashSet<>(type1s);

    if(CollectionUtils.isEmpty(type1s) && CollectionUtils.isEmpty(type2s)){
      result.add(constructSearchParameter(0, 0));
      //System.out.println("splitByType.SIZE:" + result.size());
      return result;
    }

    for(int c : type2s){
      if(map.containsKey(c)) {
        int p = map.get(c);
        if (type1s.contains(p)) {
          result.add(constructSearchParameter(p, c));
          type1copy.remove(p);
        } else {
          result.add(constructSearchParameter(0, c));
        }
      }else {
        continue;
      }
    }

    for(int c1 : type1copy){

      result.add(constructSearchParameter(c1, 0));

    }

    //System.out.println("splitByType.SIZE:"+result.size());
    if(result.size() == 0){
      //result.add(constructSearchParameter(0, 0));
      throw new InvalidParameterException("context:"+context+",type1:"+type1+",type2:"+type2+",类目不存在，请检查后重新输入");

    }
    return result;
  }

  public List<SearchParameter> splitByTypeEx() throws Exception{
    Map<Integer, Integer> map = MutableCaches.getInstance().PRODUCT_TYPE_MAP.getCacheContent();
    List<SearchParameter> result = new ArrayList<>();
    Set<Integer> type1copy = new HashSet<>(type1s);

    if(CollectionUtils.isEmpty(type1s) && CollectionUtils.isEmpty(type2s)){
      result.add(constructSearchParameter2(0, 0));
      return result;
    }

    for(int c : type2s){
      if(map.containsKey(c)) {
        int p = map.get(c);
        if (type1s.contains(p)) {
          result.add(constructSearchParameter2(p, c));
          type1copy.remove(p);
        } else {
          result.add(constructSearchParameter2(0, c));
        }
      }else {
        continue;
      }
    }

    for(int c1 : type1copy){

      result.add(constructSearchParameter2(c1, 0));

    }

    //System.out.println("splitByType.SIZE:"+result.size());
    if(result.size() == 0){
      //result.add(constructSearchParameter(0, 0));
      throw new InvalidParameterException("context:"+context+",type1:"+type1+",type2:"+type2+",类目不存在，请检查后重新输入");

    }
    return result;
  }

  public SearchParameter constructSearchParameter(int type1Int, int type2Int){
    return new SearchParameter(type1Int,type2Int,this.destinationCountryId, this.destinationCityId,
                               this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                               this.context, this.requestId, this.user, this.pageInfo, this.destination);
  }

  public SearchParameter constructSearchParameter2(int type1Int, int type2Int){
    return new SearchParameter(type1Int,type2Int,this.destinationCountryId, this.destinationCityId,
                               this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                               this.context, this.requestId, this.user, this.pageInfo, this.destination,
                               this.destCountry);
  }

  public SearchParameter constructSearchParameter(int type1Int, int type2Int, String country){
    return new SearchParameter(type1Int,type2Int,this.destinationCountryId, this.destinationCityId,
                               this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                               this.context, this.requestId, this.user, this.pageInfo, country);
  }
  public SearchParameter constructSearchParameterEx(int type1Int, int type2Int){
    String country = this.destCountry;
    if(destCountry == null) {
      country = MutableCaches.getInstance().CITY_COUNTRY_MAP.getCacheContent().get(destination);
    }
    if(country != null) {
      return new SearchParameter(type1Int, type2Int, this.destinationCountryId, this.destinationCityId,
                                 this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                                 this.context, this.requestId, this.user, this.pageInfo, country);
    }else {
      return null;
    }
  }

  public List<SearchParameter> constructSearchParameterEx2(int type1Int, int type2Int){
    List<String> country = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().get(destination);
    int size = 0;
    if((country != null)) {
      size = country.size();
      List<SearchParameter> searchParameters = new ArrayList<>(size);
      for(int i = 0; i < size; i++) {
        searchParameters.add(
          new SearchParameter(type1Int, type2Int, this.destinationCountryId, this.destinationCityId,
                              this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                              this.context, this.requestId, this.user, this.pageInfo, country.get(i)));
      }
      return searchParameters;
    }else {
      return null;
    }
  }

  public SearchParameter constructSearchParameterEx(int type1Int, int type2Int, String city){
    System.out.println("city:" + city);
    String country = MutableCaches.getInstance().CITY_COUNTRY_MAP.getCacheContent().get(city);
    System.out.println("country:" + country);
    if(country != null) {
      return new SearchParameter(type1Int, type2Int, this.destinationCountryId, this.destinationCityId,
                                 this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                                 this.context, this.requestId, this.user, this.pageInfo, country);
    }else {
      return null;
    }
  }

  public List<SearchParameter> constructSearchParameterEx2(int type1Int, int type2Int, String city) {
    List<String> country = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().get(city);
    int size = 0;
    if((country != null)) {
      size = country.size();
      List<SearchParameter> searchParameters = new ArrayList<>(size);
      for(int i = 0; i < size; i++) {
        searchParameters.add(
          new SearchParameter(type1Int, type2Int, this.destinationCountryId, this.destinationCityId,
                              this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                              this.context, this.requestId, this.user, this.pageInfo, country.get(i)));
      }
      return searchParameters;
    }else {
      return null;
    }
  }

  public List<SearchParameter> constructSearchParameterEx3(int type1Int, int type2Int) {
    List<String> country = null;
    if(this.destCountry != null){
      country = new ArrayList<>(1);
      country.add(this.destCountry);
    }else {
      country = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent()
                                                                 .get(this.destination);
    }
    int size = 0;
    if((country != null)) {
      size = country.size();
      //System.out.println("constructSearchParameterEx3 国家SIZE:"+size);
      List<SearchParameter> searchParameters = new ArrayList<>(size);
      for(int i = 0; i < size; i++) {
        searchParameters.add(
          new SearchParameter(type1Int, type2Int, this.destinationCountryId, this.destinationCityId,
                              this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                              this.context, this.requestId, this.user, this.pageInfo, null, country.get(i)));
      }
      return searchParameters;
    }else {
      return null;
    }
  }

  private boolean isNullParameter(){
    if(StringUtils.isBlank(type1) && StringUtils.isBlank(type2) && StringUtils.isBlank(userInputKW)
      && (destinationCountryId ==0) && (destinationCityId == 0) && (setOutPlaceId == -1)
      && (supplierId == 0) && (StringUtils.isBlank(this.destination)))
      return false;
    return true;
  }

  public SearchParameter getTaiwanSP(int t1, int t2){
    return new SearchParameter(t1, t2, this.destinationCountryId, this.destinationCityId,
                               this.setOutPlaceId, this.supplierId, this.userInputKW, this.conditions,
                               this.context, this.requestId, this.user, this.pageInfo, "台湾", null);
  }

  public void addTypes(Set<List<Word>> words){
    if(CollectionUtils.isEmpty(words))
      return;

    //System.out.println("TYPEH:"+this.type1s);
    for(List<Word> wordList: words){
      for(Word word: wordList) {
        String name = word.getName();
        //System.out.println("name:"+name);
        switch (name) {
          case "酒店":
            this.type1s.add(1017);
            this.type1s.add(1018);
            break;
          case "机票":
            this.type1s.add(1016);
            break;
          case "邮轮":
            this.type1s.add(1020);
            break;
          case "租车":
            this.type1s.add(1021);
            break;
          case "签证":
            this.type1s.add(1785);
            break;
          case "当地玩乐":
            this.type1s.add(2410);
            break;
          case "保险":
            this.type1s.add(1049);
            break;
        }
      }
    }

    //System.out.println("TYPEA:"+this.type1s);
  }

}

